# [八] Bean的实例化-多例作用域
Bean的多例作用域
关于bean的作用域,实际就是bean的管理方式,常见的作用域有五个:
- singleton(Spring 容器)
- prototype(Spring 容器)主要讲解多例
- request(Web 容器)
- session(Web 容器)
- application(Web 容器)
Spring 容器加载的时候,大部分处理的是单例bean
的实例化过程,也就是默认的bean作用域,而且Spring 容器对多例bean默认采取的是懒加载
,所以多例bean并不会实例化
,那么当BeanDefinition
中bean的属性为多例的时候,Spring 是如何处理的呢?
# 多例Bean的代码
定义一个多例模式的类
如果
SCOPE
是SCOPE_PROTOTYPE
时,不管是不是同一个线程,只要是 getBean就会得到一个新 的实例
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
}
# 多例Bean的流程
多例Bean的调用流程和单例Bean基本一致,只是没有放到缓存中,因此循环依赖会报错。
进入 doGetBean()方法,该方法是多例Bean的入口
类文件: org.springframework.beans.factory.support.AbstractBeanFactory
else {
/**
* TOTO : 处理原型模式下循环依赖的问题
* --触发场景:原型模式下,如果存在A中有B属性,B中有A属性,那么当依赖注入的时候,
* 就会产生当A还没创建完的时候,由于对B的创建再次返回创建A,造成循环依赖
*
* 如果 singletonObjects 缓存里面没有,则走下来
* 如果是 scope 是 Prototype 的,校验是否有出现循环依赖,
* 如果ThreadLocal有,则直接报错
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 检查一下这个 BeanDefinition 在容器中是否存在
BeanFactory parentBeanFactory = getParentBeanFactory();
// 如果不存在 beanDefinitionMap (一个concurrenthashmap)容器中,也就是所有已经加载的类中不包括
// beanName,则尝试从 parentBeanFactory 中检测
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果当前容器不存在这个 BeanDefinition,试试父容器中有没有
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// 使用显式参数委托给父进程。
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// 没有 args -> 委托给标准的 getBean 方法
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
继续执行下面多例逻辑的代码
类文件: org.springframework.beans.factory.support.AbstractBeanFactory
// 如果是 prototype scope 的,创建 prototype 的实例
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
// 多例bean底层是通过 ThreadLocal<Object> 来存放的,创建之前先看 //ThreadLocal<Object>是否存在,不存在则添加bean到ThreadLocal<Object> 中。
// 控制循环依赖,
beforePrototypeCreation(beanName);
// 直接返回创建对象,不会放到缓存中
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
// 该方法是 FactoryBean 接口的调用入口
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
进入createBean()方法, org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// //主要看这个方法,重要程度 5
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
进入 doCreateBean()方法, 类文件:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
# 其他作用域解读
doGetBean()方法,其他实现类来实现的作用域的入口
类文件: org.springframework.beans.factory.support.AbstractBeanFactory
// 如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
// 执行创建 Bean
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 该方法是FactoryBean接口的调用入口
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
# 自定义作用域
1、定义一个类继承
BeanFactoryPostProcessor
接口,拿到ConfigurableListableBeanFactory
类的引用,
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 设置自定义的scope 名字,和解析类
beanFactory.registerScope("jackScope",new CustomScope());
}
}
2、定义解析类
public class CustomScope implements Scope {
// 通过 ThreadLocal 来管理bean
private ThreadLocal local = new ThreadLocal();
/*
* 这个方法就是自己管理bean
* */
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if(local.get() != null) {
return local.get();
} else {
//这个方法就是掉createbean方法获得一个实例
Object object = objectFactory.getObject();
local.set(object);
return object;
}
}
}
3、定义入口类
@Component
@Scope("jackScope")
public class CustomScopeBean {
private String username;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
4、测试类
@Test
public void customScopeTest() {
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
if(finalI % 2 == 0) {
System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
}
else {
System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
}
}).start();
}
}
5、执行结果,可见同一个线程执行两次的结果hash一致
Thread-5-->com.xiangxue.jack.scope.CustomScopeBean@132d6fa
Thread-1-->com.xiangxue.jack.scope.CustomScopeBean@b4a3ea2
Thread-8-->com.xiangxue.jack.scope.CustomScopeBean@191edca8
Thread-6-->com.xiangxue.jack.scope.CustomScopeBean@32280ef5
Thread-7-->com.xiangxue.jack.scope.CustomScopeBean@7fbdeb7d
Thread-3-->com.xiangxue.jack.scope.CustomScopeBean@1601fd2a
Thread-9-->com.xiangxue.jack.scope.CustomScopeBean@399e5f47
Thread-10-->com.xiangxue.jack.scope.CustomScopeBean@283b28be
Thread-3-->com.xiangxue.jack.scope.CustomScopeBean@1601fd2a
Thread-7-->com.xiangxue.jack.scope.CustomScopeBean@7fbdeb7d
Thread-1-->com.xiangxue.jack.scope.CustomScopeBean@b4a3ea2
Thread-4-->com.xiangxue.jack.scope.CustomScopeBean@44d305aa
Thread-5-->com.xiangxue.jack.scope.CustomScopeBean@132d6fa
Thread-2-->com.xiangxue.jack.scope.CustomScopeBean@30fd9718
Thread-9-->com.xiangxue.jack.scope.CustomScopeBean@399e5f47